package ch.ms.persistenttimers;

import java.util.HashSet;
import java.util.Set;

import org.anddev.andengine.engine.Engine;

import android.content.Context;

import ch.ms.persistenttimers.PersistentTimer.PersistentTimerData;
import ch.ms.persistenttimers.actions.DefaultOnTimerFinishAction;
import ch.ms.persistenttimers.actions.DefaultOnTimerStartAction;
import ch.ms.persistenttimers.actions.DefaultOnTimerTickAction;
import ch.ms.persistenttimers.actions.PersistTimerAction;
import ch.ms.persistenttimers.persistence.IPersistentTimerPersistenceManager;
import ch.ms.persistenttimers.persistence.PersistentTimerPersistenceManager;

/*
 * Copyright 2011 dunhill

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */

public class PersistentTimerManager implements IPersistentTimerManager {
	
	private final Set<PersistentTimer> timers = new HashSet<PersistentTimer>();
	
	private transient volatile boolean initialized = false;
	
	private Engine engine = null;
	
	private IPersistentTimerPersistenceManager persistenceHandler = null;
	
	public PersistentTimerManager(Engine engine, Context context) {
		this.engine = engine;
		persistenceHandler = new PersistentTimerPersistenceManager(context);
	}
	
	@Override
	public PersistentTimer createNewEndlessTimer(float delay, boolean forceTicks) {
		try {
			return createNewTimer(delay,-1,true,forceTicks, null);
		} catch (NameAlreadyTakenException e) {
			throw new RuntimeException("This exception should never apear here!",e);
		}			
	}
	
	@Override
	public PersistentTimer createNewTickingTimer(float delay, long ticks, boolean forceTicks) {	
		try {
			return createNewTimer(delay,ticks,false,forceTicks, null);
		} catch (NameAlreadyTakenException e) {
			throw new RuntimeException("This exception should never apear here!",e);
		}		
	}
	
	@Override
	public PersistentTimer createNewEndlessTimer(float delay, boolean forceTicks, String uniqueName) throws NameAlreadyTakenException {
		return createNewTimer(delay,-1,true,forceTicks, uniqueName);	
	}
	
	@Override
	public PersistentTimer createNewTickingTimer(float delay, long ticks, boolean forceTicks, String uniqueName) throws NameAlreadyTakenException {
		return createNewTimer(delay,ticks,false,forceTicks, uniqueName);	
	}
	
	public void init() {
		recreateTimers();
	}
	
	private PersistentTimer createNewTimer(float delay, long ticks, boolean endless, boolean forceTicks, String uniqueName) throws NameAlreadyTakenException {
		
		if(uniqueName != null && uniqueName.length() > 0) {
			if(!getPersistenceHandler().checkUniqueNameAvailable(uniqueName)) {
				throw new NameAlreadyTakenException(uniqueName);
			}
			getPersistenceHandler().registerUniqueName(uniqueName);
		}
		
		long newId = getPersistenceHandler().getNewTimerId();
		
		PersistentTimerData data = new PersistentTimerData(newId, delay, ticks, 0, endless,forceTicks, uniqueName);
		
		PersistentTimer timer = createTimer(data);
		
		return timer;	
	}
	
	private PersistentTimer createTimer(PersistentTimerData data) {
		PersistentTimer newTimer = new PersistentTimer(data);
		timers.add(newTimer);
		
		newTimer.addStartAction(new DefaultOnTimerStartAction(engine));
		newTimer.addStartAction(new PersistTimerAction(this));
		
		newTimer.addTickAction(new DefaultOnTimerTickAction());
		newTimer.addTickAction(new PersistTimerAction(this));
		
		newTimer.addFinishAction(new DefaultOnTimerFinishAction(this));
		
		return newTimer;	
	}
	
	@Override
	public void recreateTimers() {
		// 
		if(!initialized) {
			initialized = true;
			for(long id : persistenceHandler.getTimerIds()) {
				PersistentTimerData data = loadTimerData(id);
				PersistentTimer timer = createTimer(data);
				engine.registerUpdateHandler(timer);
			}		
		}		
	}
	
	@Override
	public void removeTimer(PersistentTimer timer) {
		engine.unregisterUpdateHandler(timer);
		
		persistenceHandler.removeTimerData(timer.getData());

		timers.remove(timer);
	}
	
	@Override
	public void saveTimer(PersistentTimer timer) {
		persistenceHandler.persistTimerData(timer.getData());		
	}
	
	@Override
	public PersistentTimerData loadTimerData(long id) {
		return persistenceHandler.loadTimerData(id);
	}
	
	// ** Dependency setters **
	
	public void setEngine(Engine engine) {
		this.engine = engine;
	}

	public IPersistentTimerPersistenceManager getPersistenceHandler() {
		return persistenceHandler;
	}

	public void setPersistenceHandler(IPersistentTimerPersistenceManager persistenceHandler) {
		this.persistenceHandler = persistenceHandler;
	}	
}